/*
 *  Kara IM - Kara Instant Messenger
 *  Copyright (C) 2007-2008 Markus Karnik
 *
 *  This program is free software; you can redistribute it and/or modify it 
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 3 of the License, 
 *  or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 *  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
 *  for more details.
 *
 *  You should have received a copy of the GNU General Public License along 
 *  with this program; if not, see <http://www.gnu.org/licenses/>.
 *  
 */

package de.karnik.karaim;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import de.karnik.libkara.roster.ImContact;
import de.karnik.libkara.roster.ImContactListener;
import de.karnik.libkara.roster.ImMessage;
import de.karnik.libkara.roster.ImRoster;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.Handler;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.view.KeyEvent;
import android.widget.ScrollView;
import android.widget.TextView;

/**
 * This class implements the chat window activity.
 * 
 * @author Markus Karnik - markus.karnik@my-design.org
 * @version 1.0
 * @since 1.0
 */
public class KaraIMChatWindow extends Activity implements ImContactListener, InputDialogListener {

	/**
	 * The chat area.
	 */
	private TextView chatArea;
	/**
	 * The scroll area surrounding the chat area.
	 */
	private ScrollView chatScrollArea;
	/**
	 * The contact id.
	 */
	private int contactId;
    /**
     * The ImRoster object.
     */
    private ImRoster roster;
    /**
     * The ImContact object.
     */
    private ImContact contact;
    /**
     * The input dialog.
     */
    private KaraInputDialog inputDialog;
    
    /**
     * Indicator if input is enabled.
     */
    private boolean inputEnabled = true;
    /**
     * Indicator if input dialog is visible.
     */
    private boolean inputDialogVisible = false;
	
    /**
     * The message count.
     */
    private int messageCount = 0;
    
	/**
	 * The handler to post runnables.
	 */
	public final Handler listHandler = new Handler();
    
    /**
     * Runnable to update message list. 
     */
    final Runnable updateMessageList = new Runnable() {
        public void run() {
        	updateMessageList();
        }
    };
    
    /**
     * Updates the chat area with new messages. Calls updateChatAreaContent().
     */
    private void updateMessageList() {
    	updateChatAreaContent();
    }
	
    /* (non-Javadoc)
     * @see android.app.Activity#onCreate(android.os.Bundle)
     */
    protected void onCreate( Bundle icicle ) {
        super.onCreate( icicle );

        final Intent intent = getIntent();
        contactId = intent.getLaunchFlags();
        
        setContentView( R.layout.layout_chatwindow );
        
        roster = ImRoster.getInstance();
        contact = roster.getImContact( contactId );
        contact.addContactListener( this );
        contact.resetNewIncomingMessageCount();

        setTitle( contact.getUserName() + " (" + contact.getUserStatus() + ")" );
        
        chatArea = ( TextView )findViewById( R.id.chatArea );
        chatScrollArea = ( ScrollView )findViewById( R.id.chatAreaScroller );
        inputDialog = new KaraInputDialog( this, getString( R.string.type_here ) );
        inputDialog.setInputDialogListener( this );
        inputDialog.setCancelable( true );

	}
    
    /* (non-Javadoc)
     * @see android.app.Activity#onPause()
     */
    protected void onPause() {
    	// reset new messages count
    	if( contact != null )
    		contact.resetNewIncomingMessageCount();
    	
    	super.onPause();
    }
    
    /* (non-Javadoc)
     * @see android.app.Activity#onResume()
     */
    protected void onResume() {
    	super.onResume();
    	setTitle( contact.getUserName() + " (" + contact.getUserStatus() + ")" );
    	updateMessageList();
    }
    
    /* (non-Javadoc)
     * @see android.app.Activity#onStart()
     */
    protected void onStart() {
    	super.onStart();
    	setTitle( contact.getUserName() + " (" + contact.getUserStatus() + ")" );
    	updateMessageList();
    }
    
    /**
     * Updates the chat area with new messages.
     */
    private void updateChatAreaContent() {
    	
    	int currentMsgCount = messageCount;
    	
    	// get messages and set message count    	
        ImMessage[] allMessages = contact.getMessages();
        messageCount = allMessages.length;
    	
    	// check for new messages
        if( messageCount <= 0 && messageCount <= currentMsgCount )
    		return;

        if( chatArea == null )
        	return;
        
        // get new messages
        for( int i = currentMsgCount; i < messageCount; i++ ) {
        	ImMessage curMessage = allMessages[ i ];
        	
        	int type = curMessage.getType();
        	
        	switch( type ) {
        		case ImMessage.INCOMING:
        			appendMessage( layoutIncomingMessage( curMessage ) );
        			break;
        		case ImMessage.OUTGOING:
        			appendMessage( layoutOutgoingMessage( curMessage ) );
        			break;
        		case ImMessage.SYSTEM:
        			appendMessage( layoutSystemMessage( curMessage ) );
        			break;
        	}
        }
    }
    
    /**
     * Appends a new line to the chat area.
     * 
     * @param line The new line to append.
     */
    private void appendMessage( SpannableStringBuilder line ) {
    
    	int sy = 0, sv = 0, sh = 0, old_size = 0;
        Layout l = chatArea.getLayout();
        
        if( l != null ) {		
                sy = chatScrollArea.getScrollY();
                sv = chatScrollArea.getHeight();
                sh = chatArea.getHeight();
                old_size = l.getHeight();
        }

        chatArea.append( line );
        
        if( l != null ){
        	
        	int new_size = l.getHeight();
        	int delta = (new_size - old_size);
        	
        	if( sh + delta <= sv ){
        		chatScrollArea.smoothScrollTo( 0, 0 );
        	} else if( sv < sh + delta ){
        		chatScrollArea.smoothScrollTo( 0, sh + delta - sv );
        	} else if( sy + sv >= sh - chatArea.getLineHeight() ){
        		chatScrollArea.smoothScrollTo( 0, sh + delta - sv );
        	}
        }
    }
    

    /**
     * Creates a incoming SpannableStringBuilder from the given ImMessage object. 
     * 
     * @param message The incoming ImMessage to create the SpannableStringBuilder from.
     * @return The ImMessage object as SpannableStringBuilder.
     */
    private SpannableStringBuilder layoutIncomingMessage( ImMessage message ) {

     	SpannableStringBuilder complete = new SpannableStringBuilder();

     	SpannableStringBuilder time = getTimeFormatedTimeStamp( message.getTimeStamp(), ( int )0xFFFF0000 );
     	
     	SpannableString nick = new SpannableString( contact.getUserName() + ": " );
     	SpannableString text = new SpannableString( message.getMessage() + "\n" );
     	
     	nick.setSpan( new StyleSpan( Typeface.BOLD), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
     	nick.setSpan( new ForegroundColorSpan( 0xFFFF0000 ), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
     	
     	complete.append( time );
     	complete.append( nick );
     	complete.append( text );
    	
		return complete;
    }
    
    /**
     * Creates a outgoing SpannableStringBuilder from the given ImMessage object. 
     * 
     * @param message The outgoing ImMessage to create the SpannableStringBuilder from.
     * @return The ImMessage object as SpannableStringBuilder.
     */
    private SpannableStringBuilder layoutOutgoingMessage( ImMessage message ) {
    	
     	SpannableStringBuilder complete = new SpannableStringBuilder();

     	SpannableStringBuilder time = getTimeFormatedTimeStamp( message.getTimeStamp(), ( int )0xFFFFFF00 );
     	
     	SpannableString nick = new SpannableString( "Me: " );
     	SpannableString text = new SpannableString( message.getMessage() + "\n" );
     	
     	nick.setSpan( new StyleSpan( Typeface.BOLD), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
     	nick.setSpan( new ForegroundColorSpan( 0xFFFFFF00 ), 0, nick.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
     	
     	complete.append( time );
     	complete.append( nick );
     	complete.append( text );
    	
		return complete;
    }
    
    /**
     * Creates a system SpannableStringBuilder from the given ImMessage object. 
     * 
     * @param message The system ImMessage to create the SpannableStringBuilder from.
     * @return The ImMessage object as SpannableStringBuilder.
     */
    private SpannableStringBuilder layoutSystemMessage( ImMessage message ) {
    	
     	SpannableStringBuilder complete = new SpannableStringBuilder();
     	
     	SpannableStringBuilder time = getTimeFormatedTimeStamp( message.getTimeStamp(), ( int )0xFFFFFFFF );
     	SpannableString text = new SpannableString(  message.getMessage() + "\n" );

     	text.setSpan( new StyleSpan( Typeface.BOLD_ITALIC ), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
     	//text.setSpan( new RelativeSizeSpan( 0.8f ), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
		
     	complete.append( time );
     	complete.append( text );
     	
		return complete;
    }
    
    /**
     * Creates a SpannableStringBuilder from a given timestamp. 
     * 
     * @param timeStamp The time stamp to create the SpannableStringBuilder.
     * @param color The color for the SpannableStringBuilder.
     * @return The time stamp as SpannableStringBuilder.
     */
    private SpannableStringBuilder getTimeFormatedTimeStamp( long timeStamp, int color ) {
    	
    	SpannableStringBuilder timeString = new SpannableStringBuilder();
    	
		DateFormat dateFormat = new SimpleDateFormat( "KK:mm:ss a" );
		
		timeString.append( " (" );
		timeString.append( dateFormat.format( new Date( timeStamp ) ) );
		timeString.append( ") " );
		
		timeString.setSpan( new StyleSpan( Typeface.ITALIC ), 0, timeString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
		timeString.setSpan( new ForegroundColorSpan( color ), 0, timeString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE );
		
		return timeString;
		
    }
    
    /* (non-Javadoc)
     * @see android.app.Activity#dispatchKeyEvent(android.view.KeyEvent)
     */
    public boolean dispatchKeyEvent( KeyEvent keyEvent ) {
    	
    	int keyCode = keyEvent.getKeyCode();
    	
    	if( ( keyCode == KeyEvent.KEYCODE_DPAD_CENTER ) && inputEnabled ) {

    		if ( inputDialogVisible ) {
    			inputDialogVisible = false;
    		} else { 
    			inputDialog.show();
    			inputDialogVisible = true;
    		}
    	}    	
    	
    	return super.dispatchKeyEvent( keyEvent );
    }
	
	/* (non-Javadoc)
	 * @see de.karnik.libkara.roster.ImContactListener#messageArrived()
	 */
	public void messageArrived() {
		listHandler.post( updateMessageList );
	}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.roster.ImContactListener#incomingMessageArrived(de.karnik.libkara.roster.ImContact, de.karnik.libkara.roster.ImMessage)
	 */
	public void incomingMessageArrived( ImContact contact, ImMessage message ) {}
	/* (non-Javadoc)
	 * @see de.karnik.libkara.roster.ImContactListener#outgoingMessageArrived(de.karnik.libkara.roster.ImContact, de.karnik.libkara.roster.ImMessage)
	 */
	public void outgoingMessageArrived( ImContact contact, ImMessage message ) {}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.roster.ImContactListener#authProcessFinished()
	 */
	public void authProcessFinished() {
		inputEnabled = true;
	}

	/* (non-Javadoc)
	 * @see de.karnik.libkara.roster.ImContactListener#authProcessStarted()
	 */
	public void authProcessStarted() {
		inputEnabled = false;
	}

	/* (non-Javadoc)
	 * @see de.karnik.karaim.InputDialogListener#messageEntered(java.lang.String)
	 */
	public void messageEntered( String message ) {
		this.inputDialog.dismiss();

		ImMessage newMessage = new ImMessage( new StringBuffer( message ) );
		contact.addOutgoingMessage( newMessage );
	}
}
